<script lang="ts" setup>
/**
 * Props
 * @request 获取列表数据接口 --request
 * 必传
 *
 * @params 接口参数 --IObj
 *
 * @autoData params改变时是否自动重置
 * 默认 true
 *
 * @showOnGetData onShow时是否刷新数据 (调用 getPageData)
 * 默认 true
 *
 * @height 列表高度
 * 默认100%
 *
 * @mb 列表下边距
 * 默认 0
 *
 * Emits
 * @list 列表数据返回值
 *
 * @data 接口返回值
 *
 * @total rows总数
 *
 * Expose
 * @getData 获取数据
 *
 * @reset 重置列表
 *
 * @getPageData 获取从第一页开始到当前页的所有数据
 *
 * Slot 内容区域
 * Slot="mbbox"
 */
import { computed, getCurrentInstance, nextTick, reactive, ref, watch } from 'vue'
import type { ComponentInternalInstance } from 'vue'
import Image from '@C/Image.vue'
import { hasUnit } from '@U/common'
import { onShow } from '@dcloudio/uni-app'
import { PMsgLoadingClean } from '@C/Msg'
import type { IFunc, IObj, PropType } from '@/interface'

const { proxy: _this } = getCurrentInstance() as ComponentInternalInstance
const props = defineProps({
  request: {
    type: Function as PropType<IFunc>,
    required: true,
  },
  params: {
    type: Object as PropType<IObj>,
    required: false,
    default: () => ({}),
  },
  ignoreParam: {
    type: Array as PropType<Array<string>>,
    required: false,
    default: () => ([]),
  },
  autoData: {
    type: Boolean,
    required: false,
    default: true,
  },
  showOnGetData: {
    type: Boolean,
    required: false,
    default: true,
  },
  height: {
    type: String,
    required: false,
    default: '',
  },
  mb: {
    type: String,
    required: false,
    default: '0',
  },
  isExists: {
    type: Boolean,
    required: false,
    default: true,
  },
})
const emits = defineEmits(['list', 'data', 'total'])
defineExpose({
  reset,
  getData,
  getPageData,
})
const showOnGetDataState = ref(false)
onShow(() => {
  // 判断是否在onshow钩子里面再次调用getData
  if (props.showOnGetData) {
    showOnGetDataState.value = true
  }
  if (props.showOnGetData && showOnGetDataState.value) {
    getPageData()
  }
})

// 滚动区域高度
const scrollHeight = ref(<string>'')

nextTick(() => {
  uni
      .createSelectorQuery()
      .in(_this)
      .select('.prm-auto-list')
      .boundingClientRect((d: IObj) => {
        scrollHeight.value = d.height
      })
      .exec()
})

// 监听滚动操作 --下拉刷新，上滑加载
const scrollState = reactive({
  updating: false, // 是否正在刷新
  openLoad: true, // 是否开启上滑加载
  loading: false, // 是否正在加载
})
const scrolltolower = () => { if (scrollState.openLoad && !scrollState.loading) getData() }

// 列表状态
const listState = reactive(<IObj>{
  pageNumber: 1,
  pageSize: 10,
  pages: 1,
  total: 0,
})
const listStateJson = JSON.stringify(listState)
// 列表数据
const list = ref(<Array<IObj>>[])
// 获取数据
getData()
async function getData(page?: IObj) {
  try {
    scrollState.loading = true
    const params = props.params
    params?.ignoreParam?.forEach((key: string) => {
      delete params[key]
    })
    const { data } = await props.request({
      pageNumber: page?.pageNumber ?? listState.pageNumber,
      pageSize: page?.pageSize ?? listState.pageSize,
      ...params,
    })?.catch(() => {})?.finally(() => {
      PMsgLoadingClean()
      scrollState.loading = false
      scrollState.updating = false
    })
    if (data === null) {
      scrollState.openLoad = false
      return
    }
    const { pageNumber, pages, rows, total } = data
    listState.total = total
    if (page) {
      list.value = rows
      emits('list', list.value, data)
      return
    }
    // listState.total = total
    listState.pages = pages
    emits('data', data)
    emits('total', total)
    if (listState.pageNumber > listState.pages || list.value.length >= listState.total) {
      scrollState.openLoad = false
      return
    }
    listState.pageNumber = pageNumber + 1
    list.value = [...list.value, ...rows]
    if (list.value.length >= listState.total) {
      scrollState.openLoad = false
    }
    emits('list', list.value, data)
  } catch (e) {
    PMsgLoadingClean()
    console.log(e)
  }
}

// 获取从第一页到当前页的数据
function getPageData() {
  const pageSize = (listState.pageNumber - 1) * listState.pageSize
  return getData({ pageSize: pageSize > 0 ? pageSize : 10, pageNumber: 1 })
}

// 下拉刷新
async function reset() {
  if (scrollState.updating) return
  scrollState.updating = true
  scrollState.openLoad = true
  list.value = []
  const _listState = JSON.parse(listStateJson)
  Object.keys(_listState).forEach((key: string) => {
    listState[key] = _listState[key]
  })
  await getData()
}
// 自动刷新
const _params = computed(() => JSON.stringify(props?.params))
const _request = computed(() => props.request.toString())
watch(
    () => `${_params.value}${_request.value}`,
    () => {
      if (props?.autoData) reset()
    },
)

</script>

<template>
  <view :style="{height: height ? hasUnit(height) : '100%'}" class="prm-auto-list">
    <scroll-view
      :refresher-enabled="true"
      :refresher-threshold="20"
      :refresher-triggered="scrollState.updating"
      :scroll-x="false"
      :scroll-y="true"
      :show-scrollbar="true"
      :style="{height: `${scrollHeight}px`}"
      @refresherrefresh="reset"
      @scrolltolower="scrolltolower"
    >
      <template v-if="listState.total">
        <slot />
        <div :style="{height: hasUnit(`calc(50rpx + ${hasUnit(mb)})`)}" class="load_tips">
          {{ scrollState.openLoad ? '正在加载更多...' : '没有更多了' }}
        </div>
        <slot name="mbbox" />
      </template>
      <div v-else :style="{'margin-top': '100rpx', width: '100%', display: 'flex', 'align-items': 'center', 'flex-direction': 'column'}">
        <Image height="198" name="no_data" oss width="140" />
        <slot name="mbbox" />
      </div>
    </scroll-view>
  </view>
</template>

<style lang="less" scoped>
.load_tips {
  font-size: 24rpx;
  color: rgba(0, 0, 0, 0.45);
  line-height: 36rpx;
  text-align: center;
  margin-top: 20rpx;
}
</style>
